import 'server-only'; import '@theme/assets/globals.scss'; import { withSegmentDefaults } from '@akinon/next/hocs/server'; import Footer from '@theme/views/footer'; import Header from '@theme/views/header'; import RootModal from '@theme/views/root-modal'; import ClientRoot from './client-root'; import { ResolvedRootLayoutProps, Metadata } from '@akinon/next/types'; import { getSeoData } from '@akinon/next/data/server'; import PzRoot from '@akinon/next/components/pz-root'; import MobileAppToggler from '@akinon/next/components/mobile-app-toggler'; import pwaTags from '@theme/components/pwa-tags'; import { OfflineFallback } from '@theme/components/offline-fallback'; import { InstallPrompt } from '@theme/components/install-prompt'; import type { Viewport } from 'next'; import { draftMode } from 'next/headers'; import { PREVIEW_PZ_SEGMENT } from '@akinon/next/utils/preview'; import { parsePzParams } from '@akinon/next/utils/pz-segments'; import settings from '@theme/settings'; import RouteHandler from '@theme/components/route-handler'; import { PreviewAuthStateReporter } from '@akinon/pz-theme/src/chrome/preview-auth-state'; import ChromeSlot from '@akinon/pz-theme/src/chrome/chrome-slot'; import ThemeModals from '@akinon/pz-theme/src/overlays/theme-modals'; import { PLACEHOLDER_SLUGS } from '@akinon/pz-theme/src/chrome/placeholder-slugs'; import { ErrorCompositionProvider } from '@akinon/pz-theme/src/chrome/error-composition'; import { loadErrorComposition } from '@akinon/pz-theme/src/chrome/error-composition-source'; import './configure-page-context'; export const viewport: Viewport = { width: 'device-width', height: 'device-height', initialScale: 1, maximumScale: 1, userScalable: false }; export async function generateMetadata() { const { locales } = settings.localization; const languages = locales.map(({ localePath }) => ({ [localePath]: `/${localePath}` })); let result: Metadata = { alternates: { languages: languages.reduce((acc, cur) => ({ ...acc, ...cur }), {}) } }; const response = await getSeoData('/'); result = { ...result, ...pwaTags, ...response }; return result; } async function RootLayout({ locale, translations, params, children }: ResolvedRootLayoutProps) { // Widget preview indicator — reading draftMode does NOT opt the page into // dynamic rendering; it is only true behind the /api/preview bypass cookie. const { isEnabled: isPreview } = await draftMode(); // Which named preview is being viewed, so the banner says so — several can // exist side by side and they look identical otherwise. const previewId = parsePzParams(params, settings)[PREVIEW_PZ_SEGMENT] ?? ''; // Reconstructed here, above the error boundary, because the boundary is a // client component that can neither fetch nor receive server props — see // pz-theme's error-composition.tsx. Costs one memoized widget read until a // designer composes the server-error placeholder. const errorComposition = await loadErrorComposition(); return ( {isPreview && (
{previewId ? `Preview: ${previewId}` : 'Preview'} {/* Relative query-only href: exits preview and returns to the SAME page via the middleware ?preview=exit interception. */} Exit
)}
} />
{children}
} />
{/* Outside the clipping wrapper above: a composed modal is a fixed layer over the whole page, and it belongs to the layout rather than to any one route because its trigger may be in the header on a page it never appears on. */}
); } export default withSegmentDefaults(RootLayout, { segmentType: 'root-layout' });